#if defined(DA_LSM)
#ifndef BUFFER_H
#define BUFFER_H

#include <string.h>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include "rbtree.h"
#include "leveldb/slice.h"
#include "leveldb/comparator.h"
#include "leveldb/status.h"
#include "leveldb/options.h"
#include "leveldb/env.h"
#include "port/port.h"

#include "db/dbformat.h"

namespace leveldb {

// 1MB    
#define BUF_SIZE (1*1024*1024)
#define MAX_VALUE_SIZE  (1*1024*1024)	

    // Same thing as version_set.cc SaverState and Saver
    enum LogSaverState {
	NotFound,
	Found,
	Deleted,
	Corrupt,
    };
    struct LogSaver {
	LogSaverState state;
	const Comparator* ucmp;
	Slice user_key;
	std::string* value;
    };

    // rb-tree node
    struct bitem {
	struct rb_node bnode;

	char *bkey_data;  // InternalKey
	int bkey_size;
	
	uint64_t bvalue_offset; // offset in log file
	int bvalue_size;
    };

    // for write
    struct buf {
	char *data;
	int size;
	int available;
	uint64_t offset;
    };
    
    class Buffer {
    public:
        Buffer(const std::string &db_name,
	       int index,
	       uint64_t capacity) {
	    internal_comparator_ = new InternalKeyComparator(options_.comparator);
	    broot_.rb_node = NULL;
	    size_ = 0;
	    index_ = index;
	    dirty_ = false;
	    capacity_ = capacity;
	    
	    // open the buffer log file
	    std::string fname;
	    char tmp[10];
	    snprintf(tmp, 20, "%06d", index_);
	    fname = db_name;
	    if(db_name[db_name.size() - 1] != '/') {
		fname.append("/");
	    }
	    fname.append(tmp, 6);
	    fname.append(".bufferlog");
	    fd_ = open(fname.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
	    if(fd_ == -1) {
		fprintf(stderr, "open %s failed for %s in %s:%d\n",
			fname.c_str(), strerror(errno), __FILE__, __LINE__);
		exit(1);
	    }
	    write_offset_ = 0;
	    last_write_offset_ = 0;
	    // initialize the write buffer
	    wbuf_.data = (char *)malloc(BUF_SIZE);
	    if(!wbuf_.data) {
		fprintf(stderr, "malloc() failed in %s:%d\n", __FILE__, __LINE__);
		exit(1);
	    }
	    wbuf_.size = BUF_SIZE;
	    wbuf_.available = BUF_SIZE;
	    wbuf_.offset = 0;
	}

	~Buffer() {
	    delete internal_comparator_;
	    free(wbuf_.data);
	}
	
	int Insert(const Slice& key,
		   const Slice& value);
	int Erase(const Slice& key);
	// range erase
	int Erase(const Slice& start_key,
		  const Slice& end_key);
	Status Get(const Slice& key,
		   std::string *value);
	// arg: Saver
	Status Get(const Slice& key, void *arg);
	Iterator* NewIterator(Slice& start_key,
			      Slice& end_key);
	bool Contains(const Slice& key);

	struct bitem* Prev(struct bitem* current);
	struct bitem* Next(struct bitem* current);
	uint64_t Size();
	struct bitem* InternalGet(const Slice& key);

	int Sync();
	int LogRead(uint64_t offset,
		    int size,
		    char *data);
	int LogWrite(uint64_t *value_offset,
		     const Slice& key,
		     const Slice& value);
	void SetSmallest(const Slice &smallest) {
	    smallest_.assign(smallest.data(), smallest.size());
	}
	void SetLargest(const Slice &largest) {
	    largest_.assign(largest.data(), largest.size());
	}

	bool MayInsert(const Slice& key) {
	    if(size_ >= capacity_) {
		return false;
	    }
#if 0
	    if(internal_comparator_->Compare(key,
					     Slice(smallest_.c_str(), smallest_.size())) < 0) {
		return false;
	    }
	    // TODO: need to verify key>=smallest_ and key<=largest_
	    if(internal_comparator_->Compare(Slice(smallest_.c_str(), smallest_.size()),
					     Slice(largest_.c_str(), largest_.size())) <= 0) {
		if(internal_comparator_->Compare(key,
						 Slice(largest_.c_str(), largest_.size())) > 0) {
		    return false;
		}
	    } 
#endif
	    return true;
	}
	
    private:
	// key index, arranged in rb-tree
	struct rb_root broot_;
	
	const InternalKeyComparator *internal_comparator_;

	// lock, protect all the other fields
	port::Mutex mutex;

	Options options_;
	// current buffer size
	uint64_t size_;

	// the buffer capacity
	uint64_t capacity_;

	// the key range 
	std::string smallest_, largest_;

	// buffer log file discriptor
	int fd_;
	
	// next byte offset in log file
	uint64_t write_offset_;
	// the next byte which should be written to file, now in data buffer
	uint64_t last_write_offset_;
	
	int index_;

	// indicate whether there is dirty data,which should be synced to disk
	bool dirty_;

	// for write, buffer up to BUF_SIZE data
	struct buf wbuf_;
	
	class BIter;
    };
    
}
#endif
#endif
